/*
*  Analyser.cpp
*  Parser
*
*  Created by Christoph Ehscheidt on 12.02.08.
*  Copyright 2008 __MyCompanyName__. All rights reserved.
*
*/

#include "Analyser.h"


/**
*
*	Class ParserException
*
*/
ParserException::ParserException(char* msg) {
	this->msg = msg;
}

char* ParserException::getMessage() {
	return this->msg;
}


/**
*
*	Class ParserSemanticException
*
*/
ParserSemanticException::ParserSemanticException(Token* tok) : ParserException("unexpected token"){
	this->tok = tok;
}

char* ParserSemanticException::getMessage() {
	char* buffer = new char[1024];
	sprintf(buffer,"Unexpected Token '%s' - Line: %i - Column: %i", tok->getName(), tok->getLine(), tok->getColumn());
	return buffer;
}

/**
*
*	Class ParserUnexpectedEndException
*
*/
ParserUnexpectedEndException::ParserUnexpectedEndException(char* msg) : ParserException(msg){}

/**
*
*	Class Parser
*
*/
Parser::Parser() {
	this->symtable = new Symtable(SYMTABLE_ARRAY_SIZE);
	this->fsm = new ScannerFSM(symtable);
	this->tree = new Tree(RuleType::STATEMENTLIST);
	this->eof = false;

	this->evaluator = new EvaluatorImp(this->symtable);

	this->currentToken = 0;
}

Parser::~Parser() {
	delete this->symtable;
	delete this->fsm;
}

void Parser::setFile(char* filename)
{
	this->fsm->setFile(filename, BUFFER_BLOCK_SIZE);	
}

void Parser::parse() {
	this->getNextToken();

	cout << "parsing..." << endl;
	this->StatementList();

	cout << endl;

	cout << "evaluate..." << endl;
	this->tree->accept(this->evaluator);

	cout << endl;

	cout << "finished!" << endl;
}


void Parser::getNextToken() {
	
	try {	
		this->currentToken = this->fsm->getNextToken();
		
		if(this->currentToken == 0) {
			std::cout << "ENDE" << std::endl;
			this->eof = true;
		} else {
			
		}
	
	}
	
	catch (ScannerFSMSyntaxException e){
		fprintf(stderr, "%s %d:%d Lexem: %s\n", e.getMessage(), e.getLine(), e.getColumn(), e.getLexem());
		this->currentToken = 0; // doppeltes löschen in der rekursion verhindern
		this->getNextToken();
		
	}
	
}

bool Parser::checkToken(int tokenType) {
	
	if(this->eof == false && this->currentToken->getTokenTyp() == tokenType) {
		
		std::cout << this->currentToken->getName() << std::endl;

		if (this->currentToken->getTokenTyp() == TokenTyp::TOKEN_IDENT) {
			TokenIdent* token = (TokenIdent*) this->currentToken;
			std::cout << token->getLexem() << endl;
		}

		this->getNextToken();

		return true;
	} else {
		
		return false;
	}
}

bool Parser::checkTokenWithError(int tokenType) {
	if( !this->checkToken(tokenType) ) {
		if(!this->eof)
			throw ParserSemanticException(this->currentToken);
		else
			throw ParserUnexpectedEndException("Unexpected EndOfFile");
	}
	
	return true;
}

// Startsymbol
void Parser::StatementList() {
	//Knoten "STATEMENTLIST" erstellen
	this->node = new Node(RuleType::STATEMENTLIST);
	this->tree->addNode(node);
	//Zeiger auf STATEMENTLIST
	this->fixNodeL = this->node;

	//Zeiger auf STATEMENTLIST
	//this->fixNodeR = this->node->getParent();
	this->fixNodeR = this->node;

	Statement();
	StatementList2();
	
}
		
void Parser::StatementList2() {
			
	if( checkToken(TokenTyp::TOKEN_SEMICOLON) ) {
		//Knoten "STATEMENTLIST2" erstellen
		this->node = new Node(RuleType::STATEMENTLIST2);
		this->fixNodeR->addNode(node);
		//Zeiger auf STATEMENTLIST2
		this->fixNodeL = this->node;
		this->fixNodeR = this->node;

		if (this->currentToken) {
			Statement();
		}
		else {
			throw ParserUnexpectedEndException("Unexpected EndOfFile");
		}
		StatementList2();

	} else if(! this->eof) {
		throw ParserSemanticException(this->currentToken);			
	}
}

void Parser::Statement() {

	//currentToken sichern
	Token* tmp = this->currentToken;
	
	if ( checkToken(TokenTyp::TOKEN_IDENT) ) {
		
		checkTokenWithError(TokenTyp::TOKEN_ALLOCATION);

		//Knoten "Statement_1" erstellen
		this->node = new Node(RuleType::STATEMENT_1);
		this->fixNodeL->addNode(node);
		//Zeiger auf Statement_1
		this->fixNodeL = this->node;

		//Blatt "IDENT" erstellen
		this->blatt = new Blatt(tmp);
		this->fixNodeL->addNode(blatt);

		EXP();

	} else if( checkToken(TokenTyp::TOKEN_PRINT) ) {

		//Knoten "Statement_2" erstellen
		this->node = new Node(RuleType::STATEMENT_2);
		this->fixNodeL->addNode(node);
		//Zeiger auf Statement_2
		this->fixNodeL = this->node;
		
		checkTokenWithError(TokenTyp::TOKEN_OPEN_BRACKET);
		
		EXP();
		
		checkTokenWithError(TokenTyp::TOKEN_CLOSE_BRACKET);

	} else { 
		throw ParserSemanticException(this->currentToken);
	}

	//eine Ebene im Baum nach oben
	this->fixNodeL = this->fixNodeL->getParent();
}

void Parser::EXP() {
	
	//Knoten "EXP" erstellen
	this->node = new Node(RuleType::EXP);
	this->fixNodeL->addNode(node);
	//Zeiger auf EXP
	this->fixNodeL = this->node;

	EXP2();		
	OP_EXP();

	//eine Ebene im Baum nach oben
	this->fixNodeL = this->fixNodeL->getParent();
}

void Parser::EXP2() {

	Token* tmp = this->currentToken;
	
	if ( checkToken(TokenTyp::TOKEN_INTEGER) ) {
		//checkToken(TokenTyp::TOKEN_INTEGER);
		
		//Knoten "EXP2_INT" erstellen
		this->node = new Node(RuleType::EXP2_INT);
		this->fixNodeL->addNode(node);
		//Zeiger auf EXP2_INT
		this->fixNodeL = this->node;

		//Blatt "INTEGER" erstellen
		this->blatt = new Blatt(tmp);
		this->fixNodeL->addNode(blatt);

	} else if( checkToken(TokenTyp::TOKEN_REAL) ) {
		//checkToken(TokenTyp::TOKEN_REAL);

		//Knoten "EXP2_REAL" erstellen
		this->node = new Node(RuleType::EXP2_REAL);
		this->fixNodeL->addNode(node);
		//Zeiger auf EXP2_REAL
		this->fixNodeL = this->node;

		//Blatt "REAL" erstellen
		this->blatt = new Blatt(tmp);
		this->fixNodeL->addNode(blatt);

	} else if( checkToken(TokenTyp::TOKEN_MINUS) ) {
		//Knoten "EXP2_MINUS_EXP2" erstellen
		this->node = new Node(RuleType::EXP2_MINUS_EXP2);
		this->fixNodeL->addNode(node);
		//Zeiger auf EXP2_MINUS_EXP2
		this->fixNodeL = this->node;

		EXP2();
	} else if( checkToken(TokenTyp::TOKEN_SUM) ) {
		//Knoten "EXP2_SUM" erstellen
		this->node = new Node(RuleType::EXP2_SUM);
		this->fixNodeL->addNode(node);
		//Zeiger auf EXP2_SUM
		this->fixNodeL = this->node;

		checkTokenWithError(TokenTyp::TOKEN_OPEN_BRACKET);
		EXPLIST();		
		checkTokenWithError(TokenTyp::TOKEN_CLOSE_BRACKET);
		
	} else if(checkToken(TokenTyp::TOKEN_PROD) ) {
		//Knoten "EXP2_PROD" erstellen
		this->node = new Node(RuleType::EXP2_PROD);
		this->fixNodeL->addNode(node);
		//Zeiger auf EXP2_PROD
		this->fixNodeL = this->node;
		
		checkTokenWithError(TokenTyp::TOKEN_OPEN_BRACKET);
		EXPLIST();
		checkTokenWithError(TokenTyp::TOKEN_CLOSE_BRACKET);
		
	} else {
		//Knoten "EXP2_EXP3" erstellen
		this->node = new Node(RuleType::EXP2_EXP3);
		this->fixNodeL->addNode(node);
		//Zeiger auf EXP2_EXP3
		this->fixNodeL = this->node;

		EXP3();	
	}

	//eine Ebene im Baum nach oben
	this->fixNodeL = this->fixNodeL->getParent();
}

void Parser::EXP3() {
	Token* tmp = this->currentToken;

	if ( checkToken(TokenTyp::TOKEN_OPEN_BRACKET) ) {

		//Knoten "EXP3_1" erstellen
		this->node = new Node(RuleType::EXP3_1);
		this->fixNodeL->addNode(node);
		//Zeiger auf EXP3_1
		this->fixNodeL = this->node;
		
		EXP();
		checkTokenWithError(TokenTyp::TOKEN_CLOSE_BRACKET);
		
	} else  if ( checkToken(TokenTyp::TOKEN_IDENT) ){
		
		//Knoten "EXP3_2" erstellen
		this->node = new Node(RuleType::EXP3_2);
		this->fixNodeL->addNode(node);
		//Zeiger auf EXP3_2
		this->fixNodeL = this->node;

		//Blatt "Ident" erstellen
		this->blatt = new Blatt(tmp);
		this->fixNodeL->addNode(blatt);

	} else {
		throw ParserSemanticException(this->currentToken);
	}

	this->fixNodeL = this->fixNodeL->getParent();
}

void Parser::OP_EXP() {
	
	if( this->isOperator() ) {
		//Knoten "OP_EXP_1" erstellen
		this->node = new Node(RuleType::OP_EXP_1);
		this->fixNodeL->addNode(node);
		//Zeiger auf OP_EXP_1
		this->fixNodeL = this->node;

		OP();
		EXP();

		this->fixNodeL = this->fixNodeL->getParent();
		
	} else if( this->isEXP3() ){
		//Knoten "OP_EXP_2" erstellen
		this->node = new Node(RuleType::OP_EXP_2);
		this->fixNodeL->addNode(node);
		//Zeiger auf OP_EXP_2
		this->fixNodeL = this->node;

		EXP3();

		this->fixNodeL = this->fixNodeL->getParent();
		
	} else if(!this->eof) { 

		//??????????? aufgelöst
		// Wenn nicht EndOfFile und kein Operator und kein EXP3 folgt, dann dürfen nur noch
		// die unten aufgelisteten Tokens folgen. Ansonsten Syntaxerror!
		switch (this->currentToken->getTokenTyp()) {
			case TokenTyp::TOKEN_SEMICOLON:
			case TokenTyp::TOKEN_CLOSE_BRACKET:

			// Komma wegen EXP_LIST 2
			case TokenTyp::TOKEN_KOMMA:
				break;
			default:
				throw ParserSemanticException(this->currentToken);
		}
	}
	
	// Es darf nicht eine Ebene nach oben gegangen werden, da EXP bereits beide Äste voll hat
	// Diese Zeile würde OP_EXP einfach überschreiben.......verstößt also gegen Regel EXP2 OP_EXP
	//this->fixNodeL = this->fixNodeL->getParent();
}

void Parser::EXPLIST() {
	//Knoten "EXPLIST" erstellen
	this->node = new Node(RuleType::EXPLIST);
	this->fixNodeL->addNode(node);
	//Zeiger auf EXPLIST
	this->fixNodeL = this->node;

	EXP();
	EXPLIST2();

	this->fixNodeL = this->fixNodeL->getParent();
}

void Parser::EXPLIST2() {
	if( checkToken(TokenTyp::TOKEN_KOMMA) ) {
		//Knoten "EXPLIST2" erstellen
		this->node = new Node(RuleType::EXPLIST2);
		this->fixNodeL->addNode(node);
		//Zeiger auf EXPLIST2
		this->fixNodeL = this->node;

		EXP();
		EXPLIST2();

		this->fixNodeL = this->fixNodeL->getParent();
	} else if(!this->eof) { 

		//????????????????????? aufgelöst
		switch (this->currentToken->getTokenTyp()) {
			case TokenTyp::TOKEN_CLOSE_BRACKET:
				break;
			default:
				throw ParserSemanticException(this->currentToken);
		}
	}
}

void Parser::OP() {
	
	switch(this->currentToken->getTokenTyp()) {
		
		case TokenTyp::TOKEN_PLUS:
			checkToken(TokenTyp::TOKEN_PLUS);
			//Blatt "OP_PLUS" erstellen
			this->blatt = new Blatt(RuleType::OP_PLUS);
			this->fixNodeL->addNode(blatt);
			break;
		case TokenTyp::TOKEN_MINUS:
			checkToken(TokenTyp::TOKEN_MINUS);
			//Blatt "OP_MINUS" erstellen
			this->blatt = new Blatt(RuleType::OP_MINUS);
			this->fixNodeL->addNode(blatt);
			break;
		case TokenTyp::TOKEN_MULTIPLICATION:
			checkToken(TokenTyp::TOKEN_MULTIPLICATION);
			//Blatt "OP_MULT" erstellen
			this->blatt = new Blatt(RuleType::OP_MULT);
			this->fixNodeL->addNode(blatt);
			break;
		case TokenTyp::TOKEN_DIVISION:
			checkToken(TokenTyp::TOKEN_DIVISION);
			//Blatt "OP_DIV" erstellen
			this->blatt = new Blatt(RuleType::OP_DIV);
			this->fixNodeL->addNode(blatt);
			break;
		case TokenTyp::TOKEN_DURCH:
			checkToken(TokenTyp::TOKEN_DURCH);
			//Blatt "OP_DURCH" erstellen
			this->blatt = new Blatt(RuleType::OP_DURCH);
			this->fixNodeL->addNode(blatt);
			break;
		case TokenTyp::TOKEN_MODULO:
			{
				Token* token = this->currentToken;
				checkToken(TokenTyp::TOKEN_MODULO);
				//Blatt "OP_MOD" erstellen
				this->blatt = new Blatt(RuleType::OP_MOD);
				this->blatt->setToken(token);
				this->fixNodeL->addNode(blatt);
				break;
			}
			
		default:
			throw ParserSemanticException(this->currentToken);

	}

	//this->fixNodeL = this->fixNodeL->getParent();
}


/*
Hilfsfunktionen
*/

bool Parser::isOperator() {
	
	if(this->currentToken == 0)
		return false;
	
	switch(this->currentToken->getTokenTyp()) {
		
		case TokenTyp::TOKEN_PLUS:
		case TokenTyp::TOKEN_MINUS:
		case TokenTyp::TOKEN_MULTIPLICATION:
		case TokenTyp::TOKEN_DIVISION:
		case TokenTyp::TOKEN_DURCH:
		case TokenTyp::TOKEN_MODULO:
			
			return true;
			break;
		default:
			return false;
			
	}
}

bool Parser::isEXP3() {
	if(this->currentToken == 0)
		return false;
	
	switch(this->currentToken->getTokenTyp()) {
		
		case TokenTyp::TOKEN_OPEN_BRACKET:
		case TokenTyp::TOKEN_IDENT:
		
			return true;
			break;
		default:
			return false;
			
	}
}